//
// Created by F1997 on 2024/10/21.
//

#include "CompareTraffic.h"


// 行情1先到包的计数器
std::atomic<int> Line1Count(0);
// 行情2先到包的计数器
std::atomic<int> Line2Count(0);


std::string CompareTraffic::timevalToString(const struct timeval& tv) {
    std::tm tm = *std::gmtime(&tv.tv_sec); // 将秒转换为 tm 结构体
    std::ostringstream oss;
    oss << std::put_time(&tm, "%Y-%m-%d %H:%M:%S") << '.' << std::setfill('0') << std::setw(6) << tv.tv_usec;
    return oss.str();
}

std::string CompareTraffic::timevalToString2(const struct timeval& tv) {
    std::time_t seconds = tv.tv_sec + 8 * 3600; // utc+8时区
    std::tm* ptm = std::gmtime(&seconds); // 将秒转换为 tm 结构体
    char buffer[64];
    std::strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", ptm);

    std::ostringstream oss;
    oss << buffer << '.' << std::setfill('0') << std::setw(6) << tv.tv_usec; // 解决毫秒前面有0时，转字符串丢失的问题
    return oss.str();
}



// 写行情1，接收一个pcap包，把需要的数据写入到消息队列中
void CompareTraffic::writeTraffic1(struct pcap_pkthdr header) {
    //std::cout << "WriteTraffic1 " << header.ts.tv_sec << " " << header.caplen << header.len << std::endl;
    Line1PkgCount++; // 总包数+1
    msg_buffer msg;
    msg.msg_type = 1; // 设置消息类型
    std::string message = "L1 "
            + timevalToString2(header.ts) // 日期，到纳秒 = 固定长度
            + " " + std::to_string(Line1PkgCount) // 接收端 包编号 = 变长
            + " " + std::to_string(header.ts.tv_sec) // 秒级时间戳 = 固定长度
            + " " + std::to_string(header.caplen) // 变长
            + " " + std::to_string(header.len); // 变长
    std::strncpy(msg.msg, message.c_str(), sizeof(msg.msg) - 1);
    //std::strncpy(msg.msg2, message.c_str(), sizeof(msg.msg) - 1);

    // 发送消息到消息队列，msgsnd为阻塞函数
    //  IPC_NOWAIT ：当消息队列满时， msgsnd() 会立即返回，不会阻塞。
    //  - msgflg |= IPC_NOWAIT ：与 IPC_NOWAIT 等价，设置此选项后，发送操作将不会阻塞
    if ( msgsnd(msgid1, &msg, sizeof(msg.msg), 0) == -1 ) { // 0=阻塞，IPC_NOWAIT=非阻塞
        perror("msgsnd= ");
        exit(1);
    }
    //std::cout << "写队列 " << " sent message: " << msg.msg << std::endl;
}


// 写行情2
void CompareTraffic::writeTraffic2(struct pcap_pkthdr header) {

    //std::cout << "WriteTraffic1 " << header.ts.tv_sec << " " << header.caplen << header.len << std::endl;
    Line2PkgCount++;
    msg_buffer msg;
    msg.msg_type = 1; // 设置消息类型
    std::string message = "L2 "
            + timevalToString2(header.ts)
            + " " + std::to_string(Line2PkgCount)
            + " " + std::to_string(header.ts.tv_sec)
            + " " + std::to_string(header.caplen)
            + " " + std::to_string(header.len);

    std::strncpy(msg.msg, message.c_str(), sizeof(msg.msg) - 1);

    // 发送消息到消息队列，msgsnd为阻塞函数
    //  IPC_NOWAIT ：当消息队列满时， msgsnd() 会立即返回，不会阻塞。
    //  - msgflg |= IPC_NOWAIT ：与 IPC_NOWAIT 等价，设置此选项后，发送操作将不会阻塞
    if ( msgsnd(msgid2, &msg, sizeof(msg.msg), 0) == -1 ) { // 0=阻塞，IPC_NOWAIT=非阻塞
        perror("msgsnd= ");
        exit(1);
    }
    //std::cout << "写队列 sent message: " << msg.msg << std::endl;

}

// 读取行情1，从消息队列中读取数据
void CompareTraffic::readTraffic1(std::shared_ptr<spdlog::logger> my_logger) {
    my_logger->info("Reader started.");
    //std::cout << "Reader started." << std::endl;
    // 获取消息队的状态
    struct msqid_ds buf;
    if (msgctl(msgid1, IPC_STAT, &buf) != -1) {
        //std::cout << "There are " << buf.msg_qnum << " messages in the queue." << std::endl;
        my_logger->info("当前队列编号 {}，当前队列中的消息数量{}", msgid1, buf.msg_qnum);
    } else {
        my_logger->error("msgctl - 1 队列报错");
        perror("msgctl - 1");
        exit(1);
    }
    struct msqid_ds buf2;
    if (msgctl(msgid2, IPC_STAT, &buf2) != -1) {
        //std::cout << "There are " << buf2.msg_qnum << " messages in the queue." << std::endl;
        my_logger->info("当前队列编号 {}，当前队列中的消息数量{}", msgid2, buf2.msg_qnum);
    } else {
        my_logger->error("msgctl - 1 队列报错");
        perror("msgctl - 1");
        exit(1);
    }

    msg_buffer rcv_msg1;
    msg_buffer rcv_msg2;
    int i = 0;
    int j = 0;
    while (true ) {
        // 使用非短路与判断队列是否为空，避免阻塞
        my_logger->info("消费者 - 共读取了 {}", i);
        // 阻塞模式读 1
        if (msgrcv(msgid1, &rcv_msg1, sizeof(rcv_msg1.msg), 1, 0) != -1) { // IPC_NOWAIT
            i++;
            //std::cout << "共读取了 " << i << " 条消息." << "1 - 从队列总获取的 message: " << rcv_msg1.msg << " = " << rcv_msg1.msg2 << std::endl;
        } else {
            // 如果消息队列为空，退出循环
            std::cout << "队列为空，退出循环." << std::endl;
            //break;
            continue;
        }
        // 阻塞模式读 2
        if (msgrcv(msgid2, &rcv_msg2, sizeof(rcv_msg2.msg), 1, 0) != -1) { // IPC_NOWAIT
            j++;
            //std::cout << "共读取了 " << j << " 条消息." << "2 - 从队列总获取的 message: " << rcv_msg2.msg << std::endl;
        } else {
            // 如果消息队列为空，退出循环
            std::cout << "队列为空，退出循环." << std::endl;
            //break;
            continue;
        }
        // ==================================================================
        // 按照行情编号，包到达编号，包大小，对比每个报到达的时间
        std::cout << "开始统计。。。" ;
        contrast(rcv_msg1.msg, rcv_msg2.msg, my_logger, i , j);

    }


    // 获取消息队的状态
    struct msqid_ds buf3;
    if (msgctl(msgid1, IPC_STAT, &buf3) != -1) {
        //std::cout << "There are " << buf2.msg_qnum << " messages in the queue." << std::endl;
        my_logger->info("当前队列编号 {}，当前队列中的消息数量{}", msgid1, buf3.msg_qnum);
    } else {
        my_logger->error("msgctl - 2 队列报错");
        perror("msgctl - 2");
        exit(1);
    }
    struct msqid_ds buf4;
    if (msgctl(msgid2, IPC_STAT, &buf4) != -1) {
        //std::cout << "There are " << buf2.msg_qnum << " messages in the queue." << std::endl;
        my_logger->info("当前队列编号 {}，当前队列中的消息数量{}", msgid2, buf4.msg_qnum);
    } else {
        my_logger->error("msgctl - 1 队列报错");
        perror("msgctl - 1");
        exit(1);
    }

    // 删除消息队列
    my_logger->info("关闭消息队列");
    msgctl(msgid1, IPC_RMID, NULL);
    msgctl(msgid2, IPC_RMID, NULL);
}


// 读取行情2


// 解析消息，并比较，更新快慢计数器
// L2 2024-11-07 15:45:00.783919940 2428 1730965500 330 330
void CompareTraffic::contrast(const char *msg1, const char *msg2, std::shared_ptr<spdlog::logger> my_logger, int i, int j) {

/*    for (; 1==1;) // 持续检查 m_is_running 是否为真，该循环会一直执行直到 m_is_running 为假
        // 类似 while(d)-do
    {

    }*/
    time_t tt = time(NULL);
    struct tm *t = localtime(&tt);


    // 将 char 数组转换为 std::string
    std::string str1(msg1);
    std::string str2(msg2);

    // 输出转换后的字符串
    //std::cout << "Converted string: " << str1 << " = " << str2 << std::endl;
    std::vector<std::string> a1 = splitBySpace(str1);
    std::vector<std::string> a2 = splitBySpace(str2);

    // 直接用char数组进行对比 = 直接用字符串类型的数字进行比较，但是为字典序，不同于数值大小
    // 包编号
    long long pkgN1 = std::stoll(a1.at(3));
    long long pkgN2 = std::stoll(a2.at(3));
    // 包时间戳，纳秒部分
    long long pkgT1 = std::stoll(a1.at(2).substr(9,9));
    long long pkgT2 = std::stoll(a2.at(2).substr(9,9));
    // 包长度，caplen
    long long pkgl1 = std::stoll(a1.at(5));
    long long pkgl2 = std::stoll(a2.at(5));

    //std::cout << "Converted string: " << pkgN1 << " = " << pkgC1 << std::endl;
    my_logger->info("Converted string: {} = {}", str1, str2);
    my_logger->info("Converted {} = {} = {} = {} >>> {} ll {}", pkgN1, pkgN2, pkgT1, pkgT2, pkgl1, pkgl2);
    //std::cout << "当前时间== " << currTime << std::endl;

    //if ( pkgN1 == pkgN2 && pkgl1 == pkgl2) { // 包编号，包长度，秒或毫秒时间戳相等后，再进行比较
    if ( i == j ) { // 接收端编号相等
        if (pkgT1 > pkgT2 ) {
            //++Line1Count; // 1 比2 快，
            Line1Count.fetch_add(1); // 区别？？？
        } else {
            //++Line2Count;
            Line2Count.fetch_add(1);
        }
    }

    str1.clear();
    str2.clear();

}


// 采样计算百分比 = 用同一个起点来对比行情1和2，按照配置的采样频率计算百分比
// 采样计算百分比，并写入文件。优化，按照配置的时间进行采样统计？？？
void CompareTraffic::sampledAndSave(std::shared_ptr<spdlog::logger> my_logger ) {

    std::string fileName = "save.txt";

    //FILE *saveFile;
    //saveFile = fopen(fileName.c_str(), "wt+");

    std::ofstream file(fileName);
    // 检查文件是否成功打开
    if (!file.is_open()) {
        std::cerr << "文件打开失败: " << fileName << std::endl;
        return;
    }

    int i = 0;
    while (true) {
        // 等待采样间隔
        std::this_thread::sleep_for(std::chrono::milliseconds(3000));

        // 获取当前计数器值
        int a_val = Line1Count.load();
        int b_val = Line2Count.load();

        //int a_val = Line1Count;
        //int b_val = Line2Count;

        ++i;
        // 计算百分比
        if (b_val > 0) {
            //double percentage = static_cast<double>(a_val) / (a_val + b_val) * 100.0;
            double percentage = static_cast<double>(a_val) / ( b_val) * 100.0;
            std::cout << "第 " << i << " 次统计，a/b: " << percentage << "%, " << a_val << " = " << b_val << std::endl;

            //fprintf(saveFile, "第 %d  次统计，a/b: %d,", i, percentage);
            file << "第 " << i << " 次统计，a/b: " << percentage << "%, " << a_val << " = " << b_val << std::endl;

        } else {
            std::cout <<  "第 " << i << " 次统计。没有获取到数据。" << a_val << " = " << b_val << std::endl;

        }
    }

}









